package compilers.condition;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;

import org.antlr.runtime.Token;

import compilers.Node;
import compilers.node.Identifier;
import compilers.output.ErrorOutput;
import compilers.output.WarningOutput;
import compilers.stmt.Condition;
import compilers.table.MemScope;
import compilers.types.CharType;
import compilers.types.FloatType;
import compilers.types.IntType;
import compilers.types.Type;

public class Printf extends Factor {
	String fString;
	Vector<Condition> fNodes = new Vector<Condition>();
	
	public Printf(String fString) {
		super();
		this.fString = fString;
	}
	
	public void checkParameters(Token token, boolean included) {
		if	(!included) {
			new ErrorOutput(token,"Stdio not included.");
		}
		int count = 0;
		for (int i = 0; i < fString.length(); i++) {
			if (fString.charAt(i) == '%') {
				count++;
			}
		}
		if	(count != fNodes.size()) {
			new ErrorOutput(token, "Printf wrong paramter count.");
		}
		
		char c[] = fString.toCharArray();
		for (int i = 0; i < c.length; i++) {
			if (c[i] == '%') {
				i++;
				while ((i < c.length) && (Character.isDigit(c[i]))) {
					i++;
				}
				if ((c[i] != 'c') && (c[i] != 'f') && (c[i] != 'i') && (c[i] != 's')) {
					new WarningOutput(token, "Type " + c[i] + " in printf does not correspond to a type, using integer as default.");
				}
			} 
		}
	}
	
	public Type compute() {
		return null;
	}

	public void addNode(Condition fNode){
		fNodes.add(fNode);
	}
	
	public void print(int tab) {
		for (int i=1;i<=tab;i++){System.out.print("\t");}System.out.print("|-");
		System.out.println("Printf (" + fString + ")");

		Enumeration<Condition> enumeration = fNodes.elements();
		while(enumeration.hasMoreElements()){
			((Node) enumeration.nextElement()).print(tab+1);
		}  
	}
	
	public void printArray(int index, MemScope scope, Vector<Integer> set, Identifier ident, BufferedWriter writer, char Ptype, boolean string) throws IOException {
		Vector<Integer> ind = ident.getIndices(null);
		if (index < ind.size()) {
			for (int i = 0; i < ind.get(index).intValue(); i++) {
				set.set(index, i);
				printArray((index+1), scope, set, ident, writer, Ptype, string);
			}
		} else {
			char c[] = ident.getfIdentifier().toCharArray();
			String n = "";
			for (int i = 0; i < c.length; i++) {
				if (c[i] == '[') {
					n = ident.getfIdentifier().substring(0, i);
					break;
				}
			}
			boolean end = true;
			for (int i = 0; i < set.size(); i++) {
				n += "[" + set.get(i).intValue() + "]";
				if (set.get(i).intValue() != ind.get(i).intValue()-1) {
					end = false;
				}
			}
			
			if (scope.get(n) == -1) {
				//writer.write("ldc i 0\n");
				//writer.write("out i\n");
			} else {
				writer.write("lod " + Ptype + " " + scope.getDepth(n, 0) + " " + scope.get(n) + "\n");
				if (Ptype != getfRes().getPType()) {
					writer.write("conv " + Ptype + " " + getfRes().getPType() + "\n");
				}
				writer.write("out " + getfRes().getPType() + "\n");
			}
			if (!end && !string) {
				writer.write("ldc c 44\n");
				writer.write("out c\n");
			}
		}
	}

	public void generate(BufferedWriter writer, MemScope scope) throws IOException {
		int count = 0;
		boolean returnNumber = (this.getfRes() != null);
		char c[] = fString.toCharArray();
		for (int i = 0; i < c.length; i++) {
			if (c[i] == '%') {
				i++;
				int start = i;
				while ((i < c.length) && (Character.isDigit(c[i]))) {
					i++;
				}
				
				if (c[i] == 's') {
					fNodes.get(count).setRes(new CharType(true));
					this.setRes(new CharType(true));
				}else if (c[i] == 'c') {
					fNodes.get(count).setRes(new CharType(true));
					this.setRes(new CharType(true));
				} else if (c[i] == 'f') {
					fNodes.get(count).setRes(new FloatType(true));
					this.setRes(new FloatType(true));
				} else {
					fNodes.get(count).setRes(new IntType(true));
					this.setRes(new IntType(true));
				}
				boolean array = false;
				if (fNodes.get(count) instanceof Type) {
					Type t = (Type)fNodes.get(count);
					Identifier ident = null;
					if(t.getfIdentifier() != null) {
						ident = scope.getArray(t.getfIdentifier().getfIdentifier());
					} 
					if (ident != null && (t.getfIdentifier().getIndices(null).isEmpty())) {
						array = true;
						Vector<Integer> set = new Vector<Integer>();
						Vector<Integer> ind = ident.getIndices(null);
						for (int a = 0; a < ind.size(); a++) {
							set.add(0);
						}
						if (c[i] != 's') {
							writer.write("ldc c 123\n");
							writer.write("out c\n");
						}
						printArray(0, scope, set, ident, writer, t.getPType(), (c[i] == 's'));
						if (c[i] != 's') {
							writer.write("ldc c 125\n");
							writer.write("out c\n");
						}
					} 
				} 
				if (!array) {
					fNodes.get(count).generate(writer,scope);
					if ((start < i) && (fNodes.get(count).getfRes().getPType() == 'r')) {
						int precision = Integer.valueOf(fString.substring(start, i));
						writer.write("ldc i " + precision + "\n");
						writer.write("out r i\n");
					} else {
						writer.write("out " + fNodes.get(count).getfRes().getPType() + "\n");
					}
				}
				count++;
			} else if ((c[i] == '\\') && (c[i+1] == 'n')) {
				writer.write("ldc c 10\n");
				writer.write("out c\n");
				i++;
			} else {
				writer.write("ldc c '" + c[i] + "'\n");
				writer.write("out c\n");
			}
		}
		// return number of chars
		if (returnNumber) {
			writer.write("ldc " + this.getfRes().getPType() + " " + c.length + "\n");
		} 
	}

}
